/**
 * Copyright © 2011 Creativor Studio.
 * About license information, please see LICENSE.txt.
 */
package org.creativor.rayson.transport.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.util.logging.Logger;
import org.creativor.rayson.api.TransferService;
import org.creativor.rayson.exception.IllegalServiceException;
import org.creativor.rayson.transport.api.ServiceAlreadyExistedException;
import org.creativor.rayson.transport.server.transfer.TransferConnector;
import org.creativor.rayson.util.Log;

// TODO: Auto-generated Javadoc
/**
 * An server that handling client request packets in transport layer.
 * 
 * @author Nick Zhang
 */
@ServerConfig()
public class TransportServer
{
	private static Logger LOGGER = Log.getLogger();
	private static final String DEFAULT_NAME = "Rayson server";
	private String name;
	private ServerConfig config;
	private ConnectionManager connectionManager;
	private RpcConnector connector;
	private Listener listener;
	private PacketManager packetManager;
	
	private int portNumber;
	private ServerSocketChannel socketChannel;
	
	private TransferConnector transferConnector;
	
	/**
	 * Instantiates a new transport server.
	 */
	public TransportServer()
	{
		name = DEFAULT_NAME;
		config = this.getClass().getAnnotation(ServerConfig.class);
		this.portNumber = config.portNumber();
		packetManager = new PacketManager();
		connectionManager = new ConnectionManager();
		connector = new RpcConnector(this);
	}
	
	/**
	 * Instantiates a new transport server.
	 * 
	 * @param portNumber
	 *            the port number
	 */
	public TransportServer(int portNumber)
	{
		this();
		this.portNumber = portNumber;
	}
	
	/**
	 * Gets the config.
	 * 
	 * @return the config
	 */
	protected ServerConfig getConfig()
	{
		return config;
	}
	
	/**
	 * Gets the connection manager.
	 * 
	 * @return the connection manager
	 */
	ConnectionManager getConnectionManager()
	{
		return connectionManager;
	}
	
	/**
	 * Gets the packet manager.
	 * 
	 * @return the packet manager
	 */
	PacketManager getPacketManager()
	{
		return packetManager;
	}
	
	/**
	 * Gets the port number.
	 * 
	 * @return the port number
	 */
	public int getPortNumber()
	{
		return portNumber;
	}
	
	/**
	 * Gets the rpc connector.
	 * 
	 * @return the rpc connector
	 */
	public RpcConnector getRpcConnector()
	{
		return connector;
	}
	
	/**
	 * Gets the socket channel.
	 * 
	 * @return the socket channel
	 */
	ServerSocketChannel getSocketChannel()
	{
		return socketChannel;
	}
	
	/**
	 * Gets the transfer connector.
	 * 
	 * @return the transfer connector
	 */
	TransferConnector getTransferConnector()
	{
		return transferConnector;
	}
	
	/**
	 * Gets the version.
	 * 
	 * @return the version
	 */
	public int getVersion()
	{
		return getConfig().version();
	}
	
	/**
	 * Register service.
	 * 
	 * @param service
	 *            the service
	 * @throws ServiceAlreadyExistedException
	 *             the service already existed exception
	 * @throws IllegalServiceException
	 *             the illegal service exception
	 */
	public void registerService(TransferService service) throws ServiceAlreadyExistedException,
			IllegalServiceException
	{
		this.transferConnector.registerService(service);
	}
	
	/**
	 * Start.
	 * 
	 * @throws IOException
	 *             Signals that an I/O exception has occurred.
	 */
	public void start() throws IOException
	{
		this.socketChannel = ServerSocketChannel.open();
		this.socketChannel.configureBlocking(false);
		InetSocketAddress socketAddress = new InetSocketAddress(getPortNumber());
		this.socketChannel.socket().bind(socketAddress);
		this.portNumber = socketAddress.getPort();
		this.name = this.getClass().getSimpleName() + " on: " + this.portNumber;
		this.listener = new Listener(this);
		this.listener.start();
		transferConnector = new TransferConnector(this.getName());
		this.getConnectionManager().start();
		this.getTransferConnector().start();
		LOGGER.info("Server " + this.toString() + " started....");
	}
	
	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	public String getName()
	{
		return name;
	}
	
	/**
	 * Stop this server.
	 * 
	 * @throws IOException
	 *             If got error when close the server socket channel.
	 */
	public void stop() throws IOException
	{
		this.getTransferConnector().stop();
		this.getConnectionManager().quit();
		this.listener.quit();
		// Close socket channel.
		this.socketChannel.close();
	}
	
	/**
	 * To string.
	 * 
	 * @return the string
	 */
	@Override
	public String toString()
	{
		StringBuffer sb = new StringBuffer();
		sb.append("{");
		sb.append("port: ");
		sb.append(portNumber);
		sb.append(", config: ");
		sb.append(this.getConfig().toString());
		sb.append("}");
		return sb.toString();
	}
	
}
